package proto_wearebatman;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Map;
import java.util.StringTokenizer;

public class Circuit {

    public String name;
    protected LinkedList<Ioperable> gatelist;
    protected LinkedList<Ioperable> sourcelist;
    protected boolean startedSimulation;
    protected Map table;

    public Circuit(String filename) {
        this.name = filename;
        this.sourcelist = new LinkedList<Ioperable>();
        this.gatelist = new LinkedList<Ioperable>();
        this.startedSimulation = false;

        // Proto Output
        this.init(filename);
    }

    public void resetCounters() {
        for (int i = 0; i < this.gatelist.size(); i++) {
            this.gatelist.get(i).resetCounter();
        }
    }

    public void tick(int db) {
        if (startedSimulation) {
            for (int i = 1; i <= db; i++) {
                //Main.println("Cycle " + i);
                this.resetCounters();
                for (int j = 0; j < this.sourcelist.size(); j++) {
                    this.sourcelist.get(j).operate();
                }
            }
            if(this.getClass().toString().contains("Circuit"))
                Main.println("Circuit has ticked " + db + ".");
        } else {
            Main.println("SimulationNotStarted");
        }
    }

    public void setSimulationState(boolean state) {
        this.startedSimulation = state;
        if (state == true) {
            Main.println("Simulation started.");
        } else {
            
            Main.println("Simulation stopped.");
        }
    }

    public void listAll() {
        for (int i = 0; i < this.sourcelist.size(); i++) {
            Main.println(this.sourcelist.get(i).getName() + " | " + this.sourcelist.get(i).getClass().toString().substring(this.sourcelist.get(i).getClass().toString().indexOf('.')+1) + " | " + this.sourcelist.get(i).getFinalState());
        }
        for (int i = 0; i < this.gatelist.size(); i++) {
            Main.println(this.gatelist.get(i).getName() + " | " + this.gatelist.get(i).getClass().toString().substring(this.gatelist.get(i).getClass().toString().indexOf('.')+1) + " | " + this.gatelist.get(i).getFinalState());
        }
    }

    public void list(String name) {
        try {
            String s = table.get(name).toString();
            Ioperable item = null;
            if(s.charAt(0) == 'S')
                item = this.sourcelist.get(Integer.parseInt(s.substring(1).toString()));
            else
                item = this.gatelist.get(Integer.parseInt(s.substring(1).toString()));
            Main.println(item.getName() + " | " + item.getClass().toString().substring(item.getClass().toString().indexOf('.')+1) + " | " + item.getFinalState());
        } catch (Exception e) {
            Main.println("NameNotFound");
        }
    }

    public void setSwitch(String name, String state) {
        try {
            String s = table.get(name).toString();
            Switch sw = (Switch) this.sourcelist.get(Integer.parseInt(s.substring(1).toString()));
            if (state.equals("1") || state.equals("0")) {
                sw.setState(state.equals("1") ? 1 : 0);
            } else {
                Main.println("BadState");
            }
        } catch (ClassCastException e) {
            Main.println("NameNotFound");
        }
        catch (Exception e) {
            Main.println("BadState");
        }
    }

    protected void init(String filename) {
        BufferedReader be = null;
        try {
            be = new BufferedReader(new FileReader(new File("src\\proto_wearebatman\\tests\\" + filename)));
        } catch (FileNotFoundException ex) {
            Main.println("FileNotFound");
            return;
        }

        StringBuilder content = new StringBuilder();
        try {
            String s = null;
            while ((s = be.readLine()) != null) {
                content.append(s + "\n");
            }
        } catch (Exception e) {
            Main.println("FileReadError");
        }

        Main.println("Circuit loaded from " + filename + ".\n");

        LinkedList<String> commands = new LinkedList<String>();
        StringTokenizer st = new StringTokenizer(content.toString(), "\n");

        while (st.hasMoreTokens()) {
            commands.add(st.nextToken());
        }

        ListIterator li = commands.listIterator();

        table = new HashMap();
        int tmp = 0;

        while (li.hasNext()) {
            st = new StringTokenizer(li.next().toString(), " ");
            String command = st.nextToken();
            String param1 = st.nextToken();
            String param2 = st.nextToken();
            String param3 = null;
            String param4 = null;
            String param5 = null;
            if (st.hasMoreTokens()) {
                param3 = st.nextToken();
            }
            if (st.hasMoreTokens()) {
                param4 = st.nextToken();
            }
            if (st.hasMoreTokens()) {
                param5 = st.nextToken();
            }

            if (command.equals("new")) {
                if (param1.equals("Generator")) {
                    this.sourcelist.add(new Generator(param2));
                    table.put(param2, "S" + (this.sourcelist.size() - 1));
                }
                if (param1.equals("Switch")) {
                    this.sourcelist.add(new Switch(param2));
                    table.put(param2, "S" + (this.sourcelist.size() - 1));
                }
                if (param1.equals("Wire")) {
                    this.gatelist.add(new Wire(param2));
                    table.put(param2, "G" + (this.gatelist.size() - 1));
                }
                if (param1.equals("Inverter")) {
                    this.gatelist.add(new Inverter(param2));
                    table.put(param2, "G" + (this.gatelist.size() - 1));
                }
                if (param1.equals("Led")) {
                    this.gatelist.add(new Led(param2));
                    table.put(param2, "G" + (this.gatelist.size() - 1));
                }
                if (param1.equals("AND_Gate")) {
                    this.gatelist.add(new AND_Gate(param2));
                    table.put(param2, "G" + (this.gatelist.size() - 1));
                }
                if (param1.equals("OR_Gate")) {
                    this.gatelist.add(new OR_Gate(param2));
                    table.put(param2, "G" + (this.gatelist.size() - 1));
                }
                if (param1.equals("Scope")) {
                    this.gatelist.add(new Scope(param2));
                    table.put(param2, "G" + (this.gatelist.size() - 1));
                }
                if (param1.equals("Composite")) {
                    this.gatelist.add(new Composite(param2, param3, Integer.parseInt(param4), Integer.parseInt(param5)));
                    table.put(param2, "G" + (this.gatelist.size() - 1));
                }
            }

            if (command.equals("connect")) {
                if (tmp++ == 0) {
                    Main.println();
                }

                String id = table.get(param1).toString();
                String p2 = table.get(param2).toString();
                String p3 = param3;
                String p4 = table.get(param4).toString();
                String p5 = param5;

                boolean p2w = false;

                Object par2 = null;
                if (p2.charAt(0) == 'G') {
                    try {
                        par2 = (IhasOutput) this.gatelist.get(Integer.parseInt(p2.substring(1).toString()));
                        p2w = false;
                    } catch (ClassCastException e) {
                        par2 = (Wire) this.gatelist.get(Integer.parseInt(p2.substring(1).toString()));
                        p2w = true;
                    }
                }
                if (p2.charAt(0) == 'S') {
                    par2 = (IhasOutput) this.sourcelist.get(Integer.parseInt(p2.substring(1).toString()));
                }

                IhasInput par4 = null;
                if (p4.charAt(0) == 'G') {
                    par4 = (IhasInput) this.gatelist.get(Integer.parseInt(p4.substring(1).toString()));
                }
                if (p4.charAt(0) == 'S') {
                    par4 = (IhasInput) this.sourcelist.get(Integer.parseInt(p4.substring(1).toString()));
                }

                if (p2w) {
                    ((Wire) this.gatelist.get(Integer.parseInt(id.substring(1).toString()))).connect((Wire) par2, par4, Integer.parseInt(p5.toString()));
                } else {
                    ((Wire) this.gatelist.get(Integer.parseInt(id.substring(1).toString()))).connect((IhasOutput) par2, Integer.parseInt(p3.toString()), par4, Integer.parseInt(p5.toString()));
                }

            }
        }

    }

    void setGenerator(String name, String data) {
        try {
            String s = table.get(name).toString();
            Generator gen = (Generator) this.sourcelist.get(Integer.parseInt(s.substring(1).toString()));
            if (data.matches("[01]+")) {
                gen.setData(data);
            } else {
                Main.println("BadState");
            }
        } catch (Exception e) {
            Main.println("NameNotFound");
        }
    }
}
